#include "crypto.h"
#include <stdint.h>
#include <cstdio>
#include <cassert>
#include <limits>
#include <stdexcept>
#include <cctype>
#include <exception>
#include "md5sha1.h"

namespace base
{
    namespace utils
    {
        using namespace std;
        string md5hex(const char* plain, size_t len)
        {
            uint8_t tmp[ZEN_MD5_HASH_SIZE];
            ZEN_LIB::md5((const uint8_t*)plain, len, tmp);
            char c[3] = { '\0' };
            std::string ciphter;
            ciphter.reserve(64);
            for (int i = 0; i < ZEN_MD5_HASH_SIZE; ++i)
            {
                std::sprintf(c, "%02x", tmp[i]);
                ciphter.append(c, 2);
            }
            return ciphter;
        }

        std::string sha1hex(const char* plain, size_t len)
        {
            uint8_t tmp[ZEN_SHA1_HASH_SIZE];
            ZEN_LIB::sha1((const uint8_t*)plain, len, tmp);
            char c[3] = { '\0' };
            std::string ciphter;
            ciphter.reserve(64);
            for (int i = 0; i < ZEN_SHA1_HASH_SIZE; ++i)
            {
                std::sprintf(c, "%02x", tmp[i]);
                ciphter.append(c, 2);
            }
            return ciphter;
        }

        void sha1(const string& input, string& output)
        {
            output.resize(ZEN_SHA1_HASH_SIZE);
            uint8_t* dst = (uint8_t*)(const_cast<char*>(output.c_str()));
            ZEN_LIB::sha1((const uint8_t*)input.c_str(), input.length(), dst);
        }

        static const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

        static const char reverse_table[128] = {
            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
            64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
            64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
            64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64
        };

        ::std::string base64_encode(const ::std::string& bindata)
        {
            using ::std::string;
            using ::std::numeric_limits;

            if (bindata.size() > (numeric_limits<string::size_type>::max() / 4u) * 3u)
            {
                throw exception("Converting too large a string to base64.");
            }

            const ::std::size_t binlen = bindata.size();
            // Use = signs so the end is properly padded.
            string retval((((binlen + 2) / 3) * 4), '=');
            ::std::size_t outpos = 0;
            int bits_collected = 0;
            unsigned int accumulator = 0;
            const string::const_iterator binend = bindata.end();

            for (string::const_iterator i = bindata.begin(); i != binend; ++i)
            {
                accumulator = (accumulator << 8) | (*i & 0xffu);
                bits_collected += 8;
                while (bits_collected >= 6)
                {
                    bits_collected -= 6;
                    retval[outpos++] = b64_table[(accumulator >> bits_collected) & 0x3fu];
                }
            }
            if (bits_collected > 0)
            {  // Any trailing bits that are missing.
                assert(bits_collected < 6);
                accumulator <<= 6 - bits_collected;
                retval[outpos++] = b64_table[accumulator & 0x3fu];
            }
            assert(outpos >= (retval.size() - 2));
            assert(outpos <= retval.size());
            return retval;
        }

        ::std::string base64_decode(const ::std::string& ascdata)
        {
            using ::std::string;
            string retval;
            const string::const_iterator last = ascdata.end();
            int bits_collected = 0;
            unsigned int accumulator = 0;

            for (string::const_iterator i = ascdata.begin(); i != last; ++i)
            {
                const int c = *i;
                if (::std::isspace(c) || c == '=')
                {
                    // Skip whitespace and padding. Be liberal in what you accept.
                    continue;
                }
                if ((c > 127) || (c < 0) || (reverse_table[c] > 63))
                {
                    throw exception("This contains characters not legal in a base64 encoded string.");
                }
                accumulator = (accumulator << 6) | reverse_table[c];
                bits_collected += 6;
                if (bits_collected >= 8)
                {
                    bits_collected -= 8;
                    retval += (char)((accumulator >> bits_collected) & 0xffu);
                }
            }
            return retval;
        }
    }
}

